<!DOCTYPE html>
<html lang="en">
    <head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta name="format-detection" content="telephone=no" />

  <title>
    44|套路篇：网络性能优化的几个思路（下） | 迪克猪的博客
  </title>

  
  <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
  <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
  <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
  <link rel="manifest" href="/manifest.json" />
  <meta name="theme-color" content="#ffffff" />

  
  <link
    rel="stylesheet"
    href="https://unpkg.com/modern-normalize@0.6.0/modern-normalize.css"
  />

  
  
  
  
  <link rel="stylesheet" href="https://zsy619.github.io/style.min.f7761d111b74dd5c07f0111decee92938c12abc42e0fd319e1a07483e248b54e.css" integrity="sha256-93YdERt03VwH8BEd7O6Sk4wSq8QuD9MZ4aB0g&#43;JItU4=" />

  
  
    
  
</head>

    <body>
        <header id="header">
  <div class="header_container">
    <h1 class="sitetitle">
      <a href="https://zsy619.github.io" title="迪克猪的博客">迪克猪的博客</a>
    </h1>
    <nav class="navbar">
      <ul>
        <li><a href="https://zsy619.github.io">Home</a></li>
        
          <li>
            <a href="/post/">
              
              <span>LINUX性能优化</span>
            </a>
          </li>
        
          <li>
            <a href="/csapp/">
              
              <span>深入理解计算机系统_第三版</span>
            </a>
          </li>
        
          <li>
            <a href="/golang/">
              
              <span>golang</span>
            </a>
          </li>
        
          <li>
            <a href="/docker/">
              
              <span>容器</span>
            </a>
          </li>
        
          <li>
            <a href="/flutter/">
              
              <span>Flutter</span>
            </a>
          </li>
        
          <li>
            <a href="/know/">
              
              <span>知识点</span>
            </a>
          </li>
        
          <li>
            <a href="/categories/">
              
              <span>目录</span>
            </a>
          </li>
        
          <li>
            <a href="/about/">
              
              <span>关于</span>
            </a>
          </li>
        
        <li class="hide-sm"><a href="https://zsy619.github.io/index.xml" type="application/rss+xml">RSS</a></li>
      </ul>
    </nav>
  </div>
</header>

        
<section id="main">
  <article class="post content">
    <h2 class="title">44|套路篇：网络性能优化的几个思路（下）</h2>
    <div class="post_content">
      <h2 id="网络性能优化">网络性能优化</h2>
<h3 id="传输层">传输层</h3>
<p>TCP 提供了面向连接的可靠传输服务。要优化 TCP，我们首先要掌握 TCP 协议的基本原理，比如流量控制、慢启动、拥塞避免、延迟确认以及状态流图（如下图所示）等。</p>
<p><img src="../../images/20200707-1659-04.png" alt=""></p>
<p>第一类，在请求数比较大的场景下，你可能会看到大量处于 TIME_WAIT 状态的连接，它们会占用大量内存和端口资源。这时，我们可以优化与 TIME_WAIT 状态相关的内核选项，比如采取下面几种措施。</p>
<ul>
<li>增大处于 TIME_WAIT 状态的连接数量 net.ipv4.tcp_max_tw_buckets ，并增大连接跟踪表的大小 net.netfilter.nf_conntrack_max。</li>
<li>减小 net.ipv4.tcp_fin_timeout 和 net.netfilter.nf_conntrack_tcp_timeout_time_wait ，让系统尽快释放它们所占用的资源。开启端口复用 net.ipv4.tcp_tw_reuse。这样，被 TIME_WAIT 状态占用的端口，还能用到新建的连接中。</li>
<li>增大本地端口的范围 net.ipv4.ip_local_port_range 。这样就可以支持更多连接，提高整体的并发能力。</li>
<li>增加最大文件描述符的数量。你可以使用 fs.nr_open 和 fs.file-max ，分别增大进程和系统的最大文件描述符数；或在应用程序的 systemd 配置文件中，配置 LimitNOFILE ，设置应用程序的最大文件描述符数。</li>
</ul>
<p>第二类，为了缓解 SYN FLOOD 等，利用 TCP 协议特点进行攻击而引发的性能问题，你可以考虑优化与 SYN 状态相关的内核选项，比如采取下面几种措施。</p>
<ul>
<li>增大 TCP 半连接的最大数量 net.ipv4.tcp_max_syn_backlog ，或者开启 TCP SYN Cookies net.ipv4.tcp_syncookies ，来绕开半连接数量限制的问题（注意，这两个选项不可同时使用）。</li>
<li>减少 SYN_RECV 状态的连接重传 SYN+ACK 包的次数 net.ipv4.tcp_synack_retries。</li>
</ul>
<p>第三类，在长连接的场景中，通常使用 Keepalive 来检测 TCP 连接的状态，以便对端连接断开后，可以自动回收。但是，系统默认的 Keepalive 探测间隔和重试次数，一般都无法满足应用程序的性能要求。所以，这时候你需要优化与 Keepalive 相关的内核选项，比如：</p>
<ul>
<li>缩短最后一次数据包到 Keepalive 探测包的间隔时间 net.ipv4.tcp_keepalive_time；</li>
<li>缩短发送 Keepalive 探测包的间隔时间 net.ipv4.tcp_keepalive_intvl；</li>
<li>减少 Keepalive 探测失败后，一直到通知应用程序前的重试次数 net.ipv4.tcp_keepalive_probes。</li>
</ul>
<p><img src="../../images/20200707-1702-30.png" alt=""></p>
<p>UDP 提供了面向数据报的网络协议，它不需要网络连接，也不提供可靠性保障。所以，UDP 优化，相对于 TCP 来说，要简单得多。这里我也总结了常见的几种优化方案。</p>
<ul>
<li>跟上篇套接字部分提到的一样，增大套接字缓冲区大小以及 UDP 缓冲区范围；</li>
<li>跟前面 TCP 部分提到的一样，增大本地端口号的范围；</li>
<li>根据 MTU 大小，调整 UDP 数据包的大小，减少或者避免分片的发生。</li>
</ul>
<h3 id="网络层">网络层</h3>
<p>网络层，负责网络包的封装、寻址和路由，包括 IP、ICMP 等常见协议。在网络层，最主要的优化，其实就是对路由、 IP 分片以及 ICMP 等进行调优。</p>
<p>第一种，从路由和转发的角度出发，你可以调整下面的内核选项。</p>
<ul>
<li>在需要转发的服务器中，比如用作 NAT 网关的服务器或者使用 Docker 容器时，开启 IP 转发，即设置 net.ipv4.ip_forward = 1。</li>
<li>调整数据包的生存周期 TTL，比如设置 net.ipv4.ip_default_ttl = 64。注意，增大该值会降低系统性能。</li>
<li>开启数据包的反向地址校验，比如设置 net.ipv4.conf.eth0.rp_filter = 1。这样可以防止 IP 欺骗，并减少伪造 IP 带来的 DDoS 问题。</li>
</ul>
<p>第二种，从分片的角度出发，最主要的是调整 MTU（Maximum Transmission Unit）的大小。</p>
<p>通常，MTU 的大小应该根据以太网的标准来设置。以太网标准规定，一个网络帧最大为 1518B，那么去掉以太网头部的 18B 后，剩余的 1500 就是以太网 MTU 的大小。在使用 VXLAN、GRE 等叠加网络技术时，要注意，网络叠加会使原来的网络包变大，导致 MTU 也需要调整。比如，就以 VXLAN 为例，它在原来报文的基础上，增加了 14B 的以太网头部、 8B 的 VXLAN 头部、8B 的 UDP 头部以及 20B 的 IP 头部。换句话说，每个包比原来增大了 50B。所以，我们就需要把交换机、路由器等的 MTU，增大到 1550， 或者把 VXLAN 封包前（比如虚拟化环境中的虚拟网卡）的 MTU 减小为 1450。另外，现在很多网络设备都支持巨帧，如果是这种环境，你还可以把 MTU 调大为 9000，以提高网络吞吐量。</p>
<p>第三种，从 ICMP 的角度出发，为了避免 ICMP 主机探测、ICMP Flood 等各种网络问题，你可以通过内核选项，来限制 ICMP 的行为。比如，</p>
<ul>
<li>你可以禁止 ICMP 协议，即设置 net.ipv4.icmp_echo_ignore_all = 1。这样，外部主机就无法通过 ICMP 来探测主机。</li>
<li>或者，你还可以禁止广播 ICMP，即设置 net.ipv4.icmp_echo_ignore_broadcasts = 1。</li>
</ul>
<h3 id="链路层">链路层</h3>
<p>链路层负责网络包在物理网络中的传输，比如 MAC 寻址、错误侦测以及通过网卡传输网络帧等。自然，链路层的优化，也是围绕这些基本功能进行的。接下来，我们从不同的几个方面分别来看。</p>
<p>由于网卡收包后调用的中断处理程序（特别是软中断），需要消耗大量的 CPU。所以，将这些中断处理程序调度到不同的 CPU 上执行，就可以显著提高网络吞吐量。这通常可以采用下面两种方法。</p>
<ul>
<li>比如，你可以为网卡硬中断配置 CPU 亲和性（smp_affinity），或者开启 irqbalance 服务。</li>
<li>再如，你可以开启 RPS（Receive Packet Steering）和 RFS（Receive Flow Steering），将应用程序和软中断的处理，调度到相同 CPU 上，这样就可以增加 CPU 缓存命中率，减少网络延迟。</li>
</ul>
<p>现在的网卡都有很丰富的功能，原来在内核中通过软件处理的功能，可以卸载到网卡中，通过硬件来执行。</p>
<ul>
<li>TSO（TCP Segmentation Offload）和 UFO（UDP Fragmentation Offload）：在 TCP/UDP 协议中直接发送大包；而 TCP 包的分段（按照 MSS 分段）和 UDP 的分片（按照 MTU 分片）功能，由网卡来完成 。</li>
<li>GSO（Generic Segmentation Offload）：在网卡不支持 TSO/UFO 时，将 TCP/UDP 包的分段，延迟到进入网卡前再执行。这样，不仅可以减少 CPU 的消耗，还可以在发生丢包时只重传分段后的包。</li>
<li>LRO（Large Receive Offload）：在接收 TCP 分段包时，由网卡将其组装合并后，再交给上层网络处理。不过要注意，在需要 IP 转发的情况下，不能开启 LRO，因为如果多个包的头部信息不一致，LRO 合并会导致网络包的校验错误。</li>
<li>GRO（Generic Receive Offload）：GRO 修复了 LRO 的缺陷，并且更为通用，同时支持 TCP 和 UDP。</li>
<li>RSS（Receive Side Scaling）：也称为多队列接收，它基于硬件的多个接收队列，来分配网络接收进程，这样可以让多个 CPU 来处理接收到的网络包。</li>
<li>VXLAN 卸载：也就是让网卡来完成 VXLAN 的组包功能。</li>
</ul>
<p>对于网络接口本身，也有很多方法，可以优化网络的吞吐量。比如，</p>
<ul>
<li>你可以开启网络接口的多队列功能。这样，每个队列就可以用不同的中断号，调度到不同 CPU 上执行，从而提升网络的吞吐量。</li>
<li>再如，你可以增大网络接口的缓冲区大小，以及队列长度等，提升网络传输的吞吐量（注意，这可能导致延迟增大）。</li>
<li>你还可以使用 Traffic Control 工具，为不同网络流量配置 QoS。</li>
</ul>
<h3 id="小结">小结</h3>
<p>我们分析和定位网络瓶颈，也是基于这些网络层进行的。而定位出网络性能瓶颈后，我们就可以根据瓶颈所在的协议层，进行优化。具体而言：</p>
<ul>
<li>在应用程序中，主要是优化 I/O 模型、工作模型以及应用层的网络协议；</li>
<li>在套接字层中，主要是优化套接字的缓冲区大小；</li>
<li>在传输层中，主要是优化 TCP 和 UDP 协议；</li>
<li>在网络层中，主要是优化路由、转发、分片以及 ICMP 协议；</li>
<li>最后，在链路层中，主要是优化网络包的收发、网络功能卸载以及网卡选项。</li>
</ul>

    </div>
    <div class="info post_meta">
      <time datetime=2020-07-07T16:57:32&#43;0800 class="date">Tuesday, July 7, 2020</time>
      
        <ul class="tags">
        
          <li> <a href="https://zsy619.github.io/tags/linux%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96">linux性能优化</a> </li>
        
          <li> <a href="https://zsy619.github.io/tags/linux">linux</a> </li>
        
        </ul>
      
      
    </div>
    <div class="clearfix"></div>
  </article>
  
    <div class="other_posts">
      
      <a href="https://zsy619.github.io/post/43%E5%A5%97%E8%B7%AF%E7%AF%87%E7%BD%91%E7%BB%9C%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E7%9A%84%E5%87%A0%E4%B8%AA%E6%80%9D%E8%B7%AF%E4%B8%8A/" class="prev">43|套路篇：网络性能优化的几个思路（上）</a>
      
      
      <a href="https://zsy619.github.io/post/45%E7%AD%94%E7%96%91%E4%BA%94%E7%BD%91%E7%BB%9C%E6%94%B6%E5%8F%91%E8%BF%87%E7%A8%8B%E4%B8%AD%E7%BC%93%E5%86%B2%E5%8C%BA%E4%BD%8D%E7%BD%AE%E5%9C%A8%E5%93%AA%E9%87%8C/" class="next">45|答疑（五）：网络收发过程中，缓冲区位置在哪里？</a>
      
    </div>
    <aside id="comments">
</aside>

  
</section>

        <a id="back_to_top" title="Go To Top" href="#">
  <span>
    <svg viewBox="0 0 24 24">
      <path fill="none" d="M0 0h24v24H0z"></path>
      <path d="M12 2L4.5 20.29l.71.71L12 18l6.79 3 .71-.71z"></path>
    </svg>
  </span>
</a>

        <footer id="footer">
  <p>
    <span>&copy; 2021 <a href="https://zsy619.github.io" title="迪克猪的博客">迪克猪的博客</a> </span>
    <span>Built with <a rel="nofollow" target="_blank" href="https://gohugo.io">Hugo</a></span>
    <span>Theme by <a rel="nofollow" target="_blank" href="https://github.com/wayjam/hugo-theme-mixedpaper">WayJam</a></span>
  </p>

  <script src="https://zsy619.github.io/js/main.min.8b182175f5874aeed0acc0979345c98d4bde22208ec4f36cc1d6e3102acb4b10.js" integrity="sha256-ixghdfWHSu7QrMCXk0XJjUveIiCOxPNswdbjECrLSxA=" crossorigin="anonymous" async></script>
</footer>

    </body>
</html>
